package forward.Module.Modules.Move;


import java.awt.Color;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import forward.api.EventHandler;
import forward.api.Events.World.EventMove;
import forward.api.Events.World.EventPacketReceive;
import forward.api.Events.World.EventPostUpdate;
import forward.api.Events.World.EventPreUpdate;
import forward.api.Value.Mode;
import forward.api.Value.Numbers;
import forward.api.Value.Option;
import forward.Client;
import forward.Module.Module;
import forward.Module.ModuleType;
import forward.UI.ClientNotification;
import forward.Util.ClientUtil;
import forward.Util.Math.MathUtil;
import forward.Util.TimerUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.boss.EntityDragon;
import net.minecraft.network.play.client.C03PacketPlayer;
import net.minecraft.network.play.server.S08PacketPlayerPosLook;
import net.minecraft.potion.Potion;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovementInput;

public class Fly extends Module {
    public static int iii = 0;
    private static Numbers<Double> boost = new Numbers<Double>("Boost", "Boost", 0.0, 0.0, 3.0, 0.2);
    public Mode mode = new Mode("Mode", "Mode", (Enum[]) FlyMode.values(), (Enum) FlyMode.Vanilla);
    int counter, level;
    double moveSpeed, lastDist;
    boolean b2;
    boolean fly;
    private Option<Boolean> lagcheck = new Option<Boolean>("LagCheck", "LagCheck", true);
    private Option<Boolean> dragonvalue = new Option<Boolean>("Dragon", "Dragon", false);
    private Option<Boolean> par = new Option<Boolean>("Particle", "Particle", false);
    private TimerUtil time = new TimerUtil();
    private TimerUtil kickTimer = new TimerUtil();
    private double movementSpeed;
    private int hypixelCounter;
    private EntityDragon dragon;
    private int hypixelCounter2;
    private double flyHeight;
    private boolean dragoncrea = false;
    private int zoom;
    private int packetCounter;
    private TimerUtil deactivationDelay = new TimerUtil();

    public Fly() {
        super("Fly", new String[] { "fly", "angel" }, ModuleType.Move);
        this.setColor(new Color(158, 114, 243).getRGB());
        this.addValues(this.mode, this.boost, this.lagcheck, this.dragonvalue, this.par);
    }

    public static boolean isOnGround(double height) {
        if (!Minecraft.theWorld.getCollidingBoundingBoxes(Minecraft.thePlayer,
                Minecraft.thePlayer.getEntityBoundingBox().offset(0.0D, -height, 0.0D)).isEmpty()) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * public void damagePlayer(double d) { if (d < 1) d = 1; if (d >
     * MathHelper.floor_double(mc.thePlayer.getMaxHealth())) d =
     * MathHelper.floor_double(mc.thePlayer.getMaxHealth()); double offset = 0.0625;
     * if (mc.thePlayer != null && mc.getNetHandler() != null &&
     * mc.thePlayer.onGround) { for (int i = 0; i <= ((3 + d) / offset); i++) {
     * mc.getNetHandler().addToSendQueue(new
     * C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY
     * + offset, mc.thePlayer.posZ, false)); mc.getNetHandler().addToSendQueue(new
     * C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY,
     * mc.thePlayer.posZ, (i == ((3 + d) / offset)))); } } }
     */

    public void goToGround() {
        if (this.flyHeight > 300.0D) {
            return;
        }
        double minY = mc.thePlayer.posY - this.flyHeight;
        if (minY <= 0.0D) {
            return;
        }
        for (double y = mc.thePlayer.posY; y > minY;) {
            y -= 8.0D;
            if (y < minY) {
                y = minY;
            }
            C03PacketPlayer.C04PacketPlayerPosition packet = new C03PacketPlayer.C04PacketPlayerPosition(
                    mc.thePlayer.posX, y, mc.thePlayer.posZ, true);
            mc.thePlayer.sendQueue.addToSendQueue(packet);
        }
        for (double y = minY; y < mc.thePlayer.posY;) {
            y += 8.0D;
            if (y > mc.thePlayer.posY) {
                y = mc.thePlayer.posY;
            }
            C03PacketPlayer.C04PacketPlayerPosition packet = new C03PacketPlayer.C04PacketPlayerPosition(
                    mc.thePlayer.posX, y, mc.thePlayer.posZ, true);
            mc.thePlayer.sendQueue.addToSendQueue(packet);
        }
    }

    public void damagePlayer(int damage) {
        for (int index = 0; index < 49; index++) {
            mc.thePlayer.sendQueue.addToSendQueue(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX,
                    mc.thePlayer.posY + 0.06249D, mc.thePlayer.posZ, false));
            mc.thePlayer.sendQueue.addToSendQueue(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX,
                    mc.thePlayer.posY, mc.thePlayer.posZ, index == 49));
        }
    }

    @EventHandler
    private void onPacket(EventPacketReceive ep) {
        if (this.lagcheck.getValue().booleanValue()) {
            if (ep.getPacket() instanceof S08PacketPlayerPosLook && this.deactivationDelay.delay(2000F)) {
                ++this.packetCounter;
                S08PacketPlayerPosLook pac = (S08PacketPlayerPosLook) ep.getPacket();
                pac.yaw = mc.thePlayer.rotationYaw;
                pac.pitch = mc.thePlayer.rotationPitch;
                this.level = -5;
                if (Client.instance.getModuleManager().getModuleByClass(Fly.class).isEnabled()) {
                    Client.instance.getModuleManager().getModuleByClass(Fly.class).setEnabled(false);
                    ClientUtil.sendClientMessage("(LagbackCheck)Fly Disabled", ClientNotification.Type.ERROR);
                }
            }
        }
    }

    @Override
    public void onEnable() {
        if (!mc.thePlayer.onGround == true) {
            // ModuleManager.getModuleByClass(Fly.class).setEnabled(false);
            return;
        }
        this.zoom = 50;
        float forward = MovementInput.moveForward;
        float strafe = MovementInput.moveStrafe;
        if (this.mode.getValue() == FlyMode.Hypixel) {
            this.hypixelCounter = 0;
            this.hypixelCounter2 = 1000;
        }
        if (this.mode.getValue() == FlyMode.HypixelZoom) {
            if (Client.paiduser == false && Client.paiduser != true)
                return;
            this.damagePlayer(1);
            fly = true;
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    fly = false;
                    EventMove.setY(mc.thePlayer.motionY = 0.42);
                    this.cancel();
                }
            }, 240L);
            this.hypixelCounter = 0;
            this.hypixelCounter2 = 1000;
        }
        level = 1;
        dragoncrea = false;
        moveSpeed = 0.1D;
        b2 = true;
        lastDist = 0.0D;
    }

    @Override
    public void onDisable() {
        this.hypixelCounter = 0;
        if (this.dragonvalue.getValue().booleanValue()) {
            mc.theWorld.removeEntity(dragon);
        }
        final double posX = this.mc.thePlayer.posX;
        final double posY = this.mc.thePlayer.posY;
        final double posZ = this.mc.thePlayer.posZ;
        this.hypixelCounter2 = 100;
        this.mc.timer.timerSpeed = 1.0f;
        level = 1;
        moveSpeed = 0.1D;
        b2 = false;
        lastDist = 0.0D;
    }

    public void updateFlyHeight() {
        double h = 1.0D;
        AxisAlignedBB box = mc.thePlayer.getEntityBoundingBox().expand(0.0625D, 0.0625D, 0.0625D);
        for (this.flyHeight = 0.0D; this.flyHeight < mc.thePlayer.posY; this.flyHeight += h) {
            AxisAlignedBB nextBox = box.offset(0.0D, -this.flyHeight, 0.0D);
            if (mc.theWorld.checkBlockCollision(nextBox)) {
                if (h < 0.0625D) {
                    break;
                }
                this.flyHeight -= h;
                h /= 2.0D;
            }
        }
    }

    @EventHandler
    private void onUpdate(EventPreUpdate e) {
        this.setSuffix(this.mode.getValue());
        double speed = Math.max(3.0f, getBaseMoveSpeed());
        if (this.mode.getValue() == FlyMode.Motion) {
            mc.thePlayer.onGround = false;
            e.setOnground(isOnGround(0.001));
            if (this.mc.thePlayer.moving()) {
                if (this.mc.thePlayer.hurtTime > 15) {
                    e.setCancelled(false);
                } else {
                    new Timer().schedule(new TimerTask() {
                        @Override
                        public void run() {
                            e.setCancelled(true);
                            this.cancel();
                        }
                    }, 10L);
                }
            } else {
                e.setCancelled(false);
                this.mc.thePlayer.motionX = 0;
                this.mc.thePlayer.motionY = 0;
                this.mc.thePlayer.motionZ = 0;
            }
            if (mc.thePlayer.movementInput.jump) {
                mc.thePlayer.motionY = 0.4;
            } else if (mc.thePlayer.movementInput.sneak) {
                mc.thePlayer.motionY = -speed * 0.6;
            } else {
                mc.thePlayer.motionY = 0;
            }
            updateFlyHeight();
            mc.thePlayer.sendQueue.addToSendQueue(new C03PacketPlayer(true));
        } else if (this.mode.getValue() == FlyMode.Vanilla) {
            if (this.mc.thePlayer.moving()) {
                this.mc.thePlayer.setSpeed(3.0);
            } else {
                this.mc.thePlayer.setSpeed(0.0);
            }
            mc.thePlayer.capabilities.allowFlying = true;
        }
        mc.thePlayer.setPosition(mc.thePlayer.posX, mc.thePlayer.posY, mc.thePlayer.posZ);
        switch (counter) {
            case 1:
                break;
            case 2:
                mc.thePlayer.setPosition(mc.thePlayer.posX, mc.thePlayer.posY + 1.0E-5, mc.thePlayer.posZ);
                counter = 0;
                break;
        }
        ++counter;
        mc.thePlayer.motionY = 0;
    }

    @EventHandler
    public void onEvent(EventPostUpdate e) {
        if (this.mode.getValue() == FlyMode.HypixelZoom) {
            if (fly) {
                mc.thePlayer.motionX = 0;
                mc.thePlayer.motionZ = 0;
                mc.thePlayer.jumpMovementFactor = 0;
                mc.thePlayer.onGround = false;
            }
        }
        if (this.dragonvalue.getValue().booleanValue() && this.dragoncrea != true) {
            this.dragoncrea = true;
            dragon = new EntityDragon(mc.theWorld);
            mc.theWorld.addEntityToWorld(666, dragon);
            mc.thePlayer.ridingEntity = dragon;
        } else if (this.dragonvalue.getValue().booleanValue()) {
            final double posX4 = mc.thePlayer.posX;
            final double posX = posX4 - MathHelper.cos(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f) * 0.16f;
            final double posY = mc.thePlayer.posY;
            final double posZ2 = mc.thePlayer.posZ;
            final double posZ = posZ2 - MathHelper.sin(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f) * 0.16f;
            final float f = 0.4f;
            final float n2 = -MathHelper.sin(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f);
            final double motionX = n2 * MathHelper.cos(mc.thePlayer.rotationPitch / 180.0f * 3.1415927f) * f;
            final float cos = MathHelper.cos(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f);
            final double motionZ = cos * MathHelper.cos(mc.thePlayer.rotationPitch / 180.0f * 3.1415927f) * f;
            final double motionY = -MathHelper.sin((mc.thePlayer.rotationPitch + 2.0f) / 180.0f * 3.1415927f) * f;
            final double xCoord = posX + motionX;
            final double yCoord = posY + motionY;
            final double zCoord = posZ + motionZ;
            dragon.rotationPitch = mc.thePlayer.rotationPitch;
            dragon.rotationYaw = mc.thePlayer.rotationYawHead - 180;
            dragon.setRotationYawHead(mc.thePlayer.rotationYawHead);
            dragon.setPosition(xCoord, mc.thePlayer.posY - 2, zCoord);
        }
        if (this.par.getValue().booleanValue()) {
            final double posX4 = mc.thePlayer.posX;
            final double posX = posX4 - MathHelper.cos(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f) * 0.16f;
            final double posY = mc.thePlayer.posY;
            final double posZ2 = mc.thePlayer.posZ;
            final double posZ = posZ2 - MathHelper.sin(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f) * 0.16f;
            final float f = 0.4f;
            final float n2 = -MathHelper.sin(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f);
            final double motionX = n2 * MathHelper.cos(mc.thePlayer.rotationPitch / 180.0f * 3.1415927f) * f;
            final float cos = MathHelper.cos(mc.thePlayer.rotationYaw / 180.0f * 3.1415927f);
            final double motionZ = cos * MathHelper.cos(mc.thePlayer.rotationPitch / 180.0f * 3.1415927f) * f;
            final double motionY = -MathHelper.sin((mc.thePlayer.rotationPitch + 2.0f) / 180.0f * 3.1415927f) * f;
            for (int i = 0; i < 90; ++i) {
                final WorldClient theWorld = this.mc.theWorld;
                final EnumParticleTypes particleType = (i % 4 == 0) ? (EnumParticleTypes.CRIT_MAGIC)
                        : (new Random().nextBoolean() ? EnumParticleTypes.HEART : EnumParticleTypes.ENCHANTMENT_TABLE);
                final double xCoord = posX + motionX;
                final double yCoord = posY + motionY;
                final double zCoord = posZ + motionZ;
                final double motionX2 = mc.thePlayer.motionX;
                final double motionY2 = mc.thePlayer.motionY;
                theWorld.spawnParticle(particleType, xCoord, yCoord, zCoord, motionX2, motionY2, mc.thePlayer.motionZ,
                        new int[0]);
            }
        }
    }

    @EventHandler
    private void onMove(EventMove e) {
        if (this.mode.getValue() == FlyMode.Hypixel || this.mode.getValue() == FlyMode.HypixelZoom) {
            if(this.mode.getValue() == FlyMode.HypixelZoom) damagePlayer(1);
            this.hypixelCounter = 0;
            this.hypixelCounter2 = 1000;
            this.mc.thePlayer.motionY = 0.42f;
        } else if (this.mode.getValue() == FlyMode.Motion) {
            float forward = MovementInput.moveForward;
            float strafe = MovementInput.moveStrafe;
            float yaw = mc.thePlayer.rotationYaw;
            if (forward == 0.0F && strafe == 0.0F) {
                e.x = 0.0D;
                e.z = 0.0D;
            } else if (forward != 0.0F) {
                if (strafe >= 1.0F) {
                    yaw += (float) (forward > 0.0F ? -45 : 45);
                    strafe = 0.0F;
                } else if (strafe <= -1.0F) {
                    yaw += (float) (forward > 0.0F ? 45 : -45);
                    strafe = 0.0F;
                }

                if (forward > 0.0F) {
                    forward = 1.0F;
                } else if (forward < 0.0F) {
                    forward = -1.0F;
                }
            }
            mc.thePlayer.onGround = false;
            if (this.mc.thePlayer.isMoving()) {
                //this.mc.thePlayer.motionY = (-3) / 6.0;
                moveSpeed = 7.59D;
            } else {
                moveSpeed = 0.0D;
            }
            moveSpeed = Math.max(moveSpeed, MathUtil.getBaseMovementSpeed());
            this.mc.thePlayer.setMoveSpeed(e, moveSpeed);
        }
    }

    double getBaseMoveSpeed() {
        double baseSpeed = 0.275;
        if (this.mc.thePlayer.isPotionActive(Potion.moveSpeed)) {
            int amplifier = this.mc.thePlayer.getActivePotionEffect(Potion.moveSpeed).getAmplifier();
            baseSpeed *= 1.0 + 0.2 * (double) (amplifier + 1);
        }
        return baseSpeed;
    }

    public static enum FlyMode {
        Vanilla, Hypixel, Motion, HypixelZoom;
    }
}
